<?php
/**
 *  Yz_Session_Basic : session manager with PHP session backend
 *
 *
*/
class Yz_Session_Default
    extends Yz_Class
    implements Yz_Session_Interface
{

    /** name of session variable to store last access time
     *
     */
    const SESSION_ACCESS_TIME = '_access';
    /** session expiration time in seconds
     *  @c int
     */
    const SESSION_EXPIRES = 7200;
    /** name of session cookie
     *
     */
    const SESSION_NAME = 'SID';
    /** name of session variable to store user id
     *
     */
    const SESSION_USER_ID = '_userId';

    protected $userId;

    /**
    * Session ID
    *
    * @var string
    */
    protected $id;

    /**
    * @var static $__class__: needed for correct initialization of singletons,
    *
    */
    protected static $instance;

    /*************
    *   Methods  *
    *************/

    /**
    * getInstance() : provides Singleton pattern
    *
    */
    public static function getInstance()
    {
        debug();

        // NOTE using self, to ensure only one type of session per application
        $inst =& self::$instance;
        if ( ! $inst )
            $inst = new static;
        return $inst;
    } // getInstance()


    /**
    *
    */
    protected function __construct()
    {
        debug();

        session_name(self::SESSION_NAME);

        if (false === session_start())
            throw new Exception("Failed to start session");

        $this->id = session_id();
        $this->__dict__ =& $_SESSION;

    } // __construct()

    /**
    *
    */
    public function __destruct()
    {
        try {
            $this->finish();
        } catch ( Exception $ex ) {
            debug( "$ex" );
        }
    } // __destruct

    /**
    * getUserId() : requires session initialization
    *
    * @return int : userId which had been saved in session data
    */
    public function getUserId()
    {
        debug();

        if (null === $this->userId) {
            $UID = static::SESSION_USER_ID;

            $this->userId = isset($_SESSION[$UID]) && ! $this->expired()
                ? $_SESSION[$UID]
                : 0;
        }

        return $this->userId;
    } // getUserId


    /** get session id
     *
     * @return string
     */
    public function getId()
    {
        return $this->id;
    }


    /**
    * updateUserId() : update userId (stored in session data) on login/logout, using the User.ID
    *   no setUserId()!
    *   safety restriction: cannot set arbitrary user ids, only that set to application.user
    *
    */
    public function updateUserId( Yz_User_Interface $user=null )
    {
        debug();

        $this->userId = null === $user ? 0 : $user->getId();
        $_SESSION[static::SESSION_USER_ID] = $this->userId;
        return $this;
    } // updateUserId



    /** finish():
    *  Update last access time and store $this->vars into DB
    *
    */
    protected function finish()
    {
        debug();
        if ($this->userId > 0)
            $_SESSION[static::SESSION_ACCESS_TIME] = YZ_TIME;

    } // finish()

    protected function expired()
    {
        $at = static::SESSION_ACCESS_TIME;
        if (! isset($_SESSION[$at]))
            return false;
        return (YZ_TIME - $_SESSION[$at] > static::SESSION_EXPIRES);
    }

    /** test if the index exists in Array
     *  @param string index (after adjustIndex)
     *  @return bool
     */
    protected function _has_( $index )
    {
        return array_key_exists( $index, $_SESSION );
    }

    /** the default getter is called after index verification
     *  @param string index
     *  @return mixed|null
     */
    protected function _get_( $index )
    {
        if ($this->_has_( $index ))
            return $_SESSION[$index];
        return null;
    }

    /** the default setter is called after index verification
     *  @param string index
     *  @return chainable
     */
    protected function _set_( $index, $value )
    {
        $_SESSION[$index] = $value;
        return $this;
    }

    /** the default unsetter is called after index verification
     *  @param string index
     *  @return chainable
     */
    protected function _del_( $index )
    {
        unset($_SESSION[$index]);
        return $this;
    }

} // class

